﻿' Copyright (C) 2015 HTTPBuilder
'
' This program is free software: you can redistribute it and/or modify it under the +terms 
' of the GNU General Public License as published by the Free Software Foundation, either 
' version 3 of the License, or (at your option) any later version.

' This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
' without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
' See the GNU General Public License for more details.

' You should have received a copy of the GNU General Public License along with this program.
' If not, see http://www.gnu.org/licenses/. 

Imports HTTPBuilder.Interop

Namespace Controls
    ''' <summary>
    ''' The Graphics Device Interface (GDI) is a Microsoft Windows application programming interface and core operating system component responsible for representing graphical objects and transmitting them to output devices such as monitors and printers.
    ''' </summary>
    ''' <remarks></remarks>
    Friend NotInheritable Class GDIMemoryContext
        Implements IDisposable

#Region "Member variables"
        Private fDC, fBitmap, fStockMonoBmp As IntPtr
        Private fWidth, fHeight As Integer
        Private notDisposed As Boolean = True
#End Region

#Region "Public Methods"
        ''' <summary>
        ''' Returns a new instance of the GDI object.
        ''' </summary>
        ''' <param name="compatibleTo">Graphics object.</param>
        ''' <param name="width">Width of the GDI.</param>
        ''' <param name="height">Height of the GDI.</param>
        ''' <remarks></remarks>
        Public Sub New(compatibleTo As Graphics, width As Integer, height As Integer)
            If (IsNothing(compatibleTo) OrElse width <= 0 OrElse height <= 0) Then Throw New ArgumentException("Arguments are unacceptable")
            Dim tmp As IntPtr = compatibleTo.GetHdc()
            Dim failed As Boolean = True
            Do
                fDC = NativeMethods.CreateCompatibleDC(tmp)
                If fDC = IntPtr.Zero Then Exit Do
                fBitmap = NativeMethods.CreateCompatibleBitmap(tmp, width, height)
                If fBitmap = IntPtr.Zero Then
                    NativeMethods.DeleteDC(fDC) : Exit Do
                End If
                fStockMonoBmp = NativeMethods.SelectObject(fDC, fBitmap)
                If fStockMonoBmp = IntPtr.Zero Then
                    NativeMethods.DeleteObject(fBitmap)
                    NativeMethods.DeleteDC(fDC)
                Else
                    failed = False
                End If
            Loop While False
            compatibleTo.ReleaseHdc(tmp)
            If failed Then Throw New SystemException("GDI error occured while creating context")
            Me.fWidth = width : Me.fHeight = height
        End Sub

        ''' <summary>
        ''' Dispose method.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Dispose() Implements IDisposable.Dispose
            DisposeInternal()
            GC.SuppressFinalize(Me)
        End Sub

        ''' <summary>
        ''' Returns a new graphics object.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateGraphics() As Graphics
            Return If(Me.fDC <> IntPtr.Zero, Graphics.FromHdc(Me.fDC), Nothing)
        End Function

        ''' <summary>
        ''' Flips the BLT vertically.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub FlipVertical()
            If Me.fDC <> IntPtr.Zero Then
                NativeMethods.StretchBlt(fDC, 0, fHeight - 1, fWidth, -fHeight, fDC, 0, 0, fWidth, fHeight, NativeMethods.SRCCOPY)
            End If
        End Sub

        ''' <summary>
        ''' Returns the pixel at a given position.
        ''' </summary>
        ''' <param name="x">X position.</param>
        ''' <param name="y">Y position.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function GetPixel(ByVal x As Integer, ByVal y As Integer) As UInteger
            If fDC <> IntPtr.Zero Then
                Return NativeMethods.GetPixel(fDC, x, y)
            Else
                Throw New ObjectDisposedException(Nothing, "GDI context seems to be disposed.")
            End If
        End Function

        ''' <summary>
        ''' Gets pixel at the position.
        ''' </summary>
        ''' <param name="x">X position.</param>
        ''' <param name="y">Y position.</param>
        ''' <param name="value"></param>
        ''' <remarks></remarks>
        Public Sub SetPixel(ByVal x As Integer, ByVal y As Integer, ByVal value As UInteger)
            If fDC <> IntPtr.Zero Then
                NativeMethods.SetPixel(fDC, x, y, value)
            Else
                Throw New ObjectDisposedException(Nothing, "GDI context seems to be disposed.")
            End If
        End Sub

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="drawTo"></param>
        ''' <param name="drawRect"></param>
        ''' <remarks></remarks>
        Public Sub DrawContextClipped(ByVal drawTo As Graphics, ByVal drawRect As Rectangle)
            Do
                If (IsNothing(drawTo) OrElse fDC = IntPtr.Zero) Then Exit Do
                Dim tmpDC As IntPtr = drawTo.GetHdc()
                If tmpDC = IntPtr.Zero Then Exit Do

                NativeMethods.BitBlt(tmpDC, drawRect.Left, drawRect.Top, drawRect.Width, drawRect.Height, _
                        fDC, 0, 0, NativeMethods.SRCCOPY)

                drawTo.ReleaseHdc(tmpDC)
            Loop While False
        End Sub
#End Region

#Region "Protected Methods"
        ''' <summary>
        ''' Clears the object from memory and dispose.
        ''' </summary>
        ''' <remarks></remarks>
        Protected Overrides Sub Finalize()
            DisposeInternal()
        End Sub
#End Region

#Region "Public Properties"
        ''' <summary>
        ''' Returns the width of the GDI object.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Width() As Integer
            Get
                Return Me.fWidth
            End Get
        End Property

        ''' <summary>
        ''' Returns the height of the GDI object.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Height() As Integer
            Get
                Return Me.fHeight
            End Get
        End Property
#End Region

#Region "Private Methods"
        ''' <summary>
        ''' Internal dispose function.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub DisposeInternal()
            If notDisposed Then
                NativeMethods.SelectObject(fDC, fStockMonoBmp) 'return stock bitmap home
                NativeMethods.DeleteDC(fDC) 'deletion of memory context
                fDC = IntPtr.Zero : fStockMonoBmp = IntPtr.Zero
                NativeMethods.DeleteObject(fBitmap) 'destruction of created bitmap
                fBitmap = IntPtr.Zero
                notDisposed = False
            End If
        End Sub
#End Region
    End Class
End Namespace